﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Reflection;
using Vbyte.SharpOrm.Adapter;
using Vbyte.SharpOrm.Config;
using Vbyte.SharpOrm.SqlDb;
using Vbyte.DataSource.Cache;

namespace Vbyte.SharpOrm
{
    public static partial class OrmHelper
    {
        #region 获取单列集合
        /// <summary>
        /// 获取满足实例条件的某一属性的集合
        /// </summary>
        /// <typeparam name="D">要获取的数据类型</typeparam>
        /// <typeparam name="T">表映射实例类型</typeparam>
        /// <param name="SqlTableEntry">表映射实例</param>
        /// <param name="propName">实例的属性名称</param>
        /// <returns></returns>
        public static D[] GetArray<D, T>(this TableEntry SqlTableEntry, string propName)
            where T : TableEntry, new()
        {
            List<D> list = new List<D>();
            DataTable rTab = OrmHelper.GetDataTable(SqlTableEntry, new string[] { propName },
                null, new SqlOrderBy[0]);
            Type tDataType = typeof(D);
            foreach (DataRow row in rTab.Rows)
            {
                if (row[0] != null && row[0].GetType() != tDataType)
                {
                    list.Add((D)Convert.ChangeType(row[0], tDataType));
                }
                else
                {
                    if (row[0] != DBNull.Value)
                    {
                        list.Add((D)row[0]);
                    }
                }
            }
            return list.ToArray();
        }


        #endregion

        #region 数据列表(分页)
        /// <summary>
        /// 依据表或联接关系获取同类型的数据列表
        /// </summary>
        /// <typeparam name="T">基于TableEntry的实体类型</typeparam>
        /// <param name="SqlTableEntry">表映射实例</param>
        /// <param name="selectFields">列表获取的实体属性名集合，忽略属性将保持其默认值。</param>
        /// <param name="OrderSettings">针对提取数据的排序设置</param>
        /// <param name="filter">数据筛选委托</param>
        /// <param name="isRemove">(与过滤委托配套使用)假如设置为<c>true</c>则排除满足条件的实例，否则为增加判定。</param>
        /// <returns></returns>
        public static T[] GetDataList<T>(this TableEntry SqlTableEntry, string[] selectFields,
            SqlOrderBy[] OrderSettings, Predicate<T> filter, bool isRemove)
           where T : TableEntry, new()
        {
            return GetDataList<T>(SqlTableEntry, selectFields, null, OrderSettings, filter, isRemove);
        }

        /// <summary>
        /// 依据表或联接关系获取同类型的数据列表
        /// </summary>
        /// <typeparam name="T">基于TableEntry的实体类型</typeparam>
        /// <param name="SqlTableEntry">表映射实例</param>
        /// <param name="selectFields">列表获取的实体属性名集合，忽略属性将保持其默认值。</param>
        /// <param name="forceBindFields">强制相关属性绑定设置</param>
        /// <param name="SharedOpenConnection">已打开的共享数据库连接，只使用不关闭及销毁。</param>
        /// <param name="OrderSettings">针对提取数据的排序设置</param>
        /// <param name="filter">数据筛选委托</param>
        /// <param name="isRemove">(与过滤委托配套使用)假如设置为<c>true</c>则排除满足条件的实例，否则为增加判定。</param>
        /// <returns></returns>
        public static T[] GetDataList<T>(this TableEntry SqlTableEntry, string[] selectFields, string[] forceBindFields,
           DbConnection SharedOpenConnection, SqlOrderBy[] OrderSettings,
           Predicate<T> filter, bool isRemove)

           where T : TableEntry, new()
        {
            List<T> resultList = new List<T>();
            DbCommand cmd = GeneratorDbCommand(SqlTableEntry, SQLRowCommandType.Select, forceBindFields, selectFields, OrderSettings);
            if (SharedOpenConnection != null)
            {
                cmd.Connection = SharedOpenConnection;
                if (SharedOpenConnection.State == ConnectionState.Closed)
                {
                    SharedOpenConnection.Open();
                }
            }
            else
            {
                cmd.Connection = new AdoNetAdapter(SqlTableEntry).GetDbConnection();
                cmd.Connection.Open();
            }

            //Debug(cmd);

            DbDataReader reader = cmd.ExecuteReader();
            if (reader.HasRows)
            {
                Type t = SqlTableEntry.GetType();
                string propName = "";
                PropertyInfo[] Properties = GenericCache<PropertyInfo[]>.Get(t.FullName, () => t.GetProperties());
                while (reader.Read())
                {
                    #region 绑定新实例并添加到集合
                    T entry = new T();
                    entry.IsInCollection = true;
                    entry.SerializeOnlyList = selectFields;
                    foreach (PropertyInfo pi in Properties)
                    {
                        propName = pi.Name;
                        //所有命令操作忽略
                        if (HasAttribute(pi, typeof(CommandIgnoreAttribute), true))
                        {
                            continue;
                        }

                        //标记有映射转换
                        TableItemAttribute[] itemAttr = GetMemberInfoCustomAttributes<TableItemAttribute>(pi, true);
                        if (itemAttr.Length > 0)
                        {
                            propName = itemAttr[0].BindField;
                        }
                        try
                        {
                            //有可能没有对应字段名称
                            if (reader.GetOrdinal(propName) > -1)
                            {
                                pi.SetValue(entry, reader[propName], null);
                            }
                        }
                        catch (Exception) { }
                    }
                    #endregion

                    if (filter == null)
                    {
                        resultList.Add(entry);
                    }
                    else
                    {
                        #region 数据筛选
                        if (isRemove == true)
                        {
                            if (!filter(entry))
                            {
                                resultList.Add(entry);
                            }
                        }
                        else
                        {
                            if (filter(entry))
                            {
                                resultList.Add(entry);
                            }
                        }
                        #endregion
                    }
                }
            }

            reader.Close();
            reader.Dispose();

            //不使用共享连接，临时打开了数据操作
            if (SharedOpenConnection == null)
            {
                cmd.Connection.Close();
                cmd.Connection.Dispose();
            }
            cmd.Dispose();

            return resultList.ToArray();
        }

        /// <summary>
        /// 依据表或联接关系获取同类型的数据列表
        /// </summary>
        /// <typeparam name="T">基于TableEntry的实体类型</typeparam>
        /// <param name="SqlTableEntry">表映射实例</param>
        /// <param name="selectFields">列表获取的实体属性名集合，忽略属性将保持其默认值。</param>
        /// <param name="SharedOpenConnection">已打开的共享数据库连接，只使用不关闭及销毁。</param>
        /// <param name="OrderSettings">针对提取数据的排序设置</param>
        /// <param name="filter">数据筛选委托</param>
        /// <param name="isRemove">(与过滤委托配套使用)假如设置为<c>true</c>则排除满足条件的实例，否则为增加判定。</param>
        /// <returns></returns>
        public static T[] GetDataList<T>(this TableEntry SqlTableEntry, string[] selectFields,
            DbConnection SharedOpenConnection, SqlOrderBy[] OrderSettings,
            Predicate<T> filter, bool isRemove)
           where T : TableEntry, new()
        {
            return GetDataList<T>(SqlTableEntry, selectFields, new string[0], SharedOpenConnection, OrderSettings, filter, isRemove);
        }

        /// <summary>
        /// 获取相关实例列表的键值对表现形式
        /// </summary>
        /// <typeparam name="T">实例类型</typeparam>
        /// <param name="SqlTableEntry">表映射实例</param>
        /// <param name="selectFields">列表获取的实体属性名集合，忽略属性将保持其默认值。</param>
        /// <param name="forceBindFields">强制相关属性绑定设置</param>
        /// <param name="OrderSettings">针对提取数据的排序设置</param>
        /// <returns></returns>
        public static DictionaryEntry[][] GetEntryArrayList<T>(this T SqlTableEntry, string[] selectFields, string[] forceBindFields,
            SqlOrderBy[] OrderSettings)
            where T : TableEntry, new()
        {
            List<DictionaryEntry[]> result = new List<DictionaryEntry[]>();
            T[] entryList = GetDataList<T>(SqlTableEntry, selectFields, forceBindFields, null, OrderSettings, null, false);
            foreach (T item in entryList)
            {
                result.Add(ToDictionaryEntryArray(item, typeof(T), selectFields, false));
            }
            return result.ToArray();
        }

        /// <summary>
        /// 依据表或联接关系获取同类型的数据列表
        /// </summary>
        /// <typeparam name="T">基于TableEntry的实体类型</typeparam>
        /// <param name="SqlTableEntry">表映射实例</param>
        /// <param name="selectFields">列表获取的实体属性名集合，忽略属性将保持其默认值。</param>
        /// <param name="SharedOpenConnection">已打开的共享数据库连接，只使用不关闭及销毁。</param>
        /// <param name="OrderSettings">针对提取数据的排序设置</param>
        /// <returns>同类型的数据列表</returns>
        public static T[] GetDataList<T>(this TableEntry SqlTableEntry, string[] selectFields,
            DbConnection SharedOpenConnection, SqlOrderBy[] OrderSettings)
           where T : TableEntry, new()
        {
            return GetDataList<T>(SqlTableEntry, selectFields, SharedOpenConnection, OrderSettings, null, false);
        }

        /// <summary>
        /// 依据表或联接关系获取同类型的数据列表
        /// </summary>
        /// <typeparam name="T">基于TableEntry的实体类型</typeparam>
        /// <param name="SqlTableEntry">表映射实例</param>
        /// <param name="selectFields">列表获取的实体属性名集合，忽略属性将保持其默认值。</param>
        /// <param name="SharedOpenConnection">已打开的共享数据库连接，只使用不关闭及销毁。</param>
        /// <returns>同类型的数据列表</returns>
        public static T[] GetDataList<T>(this TableEntry SqlTableEntry, string[] selectFields, DbConnection SharedOpenConnection)
           where T : TableEntry, new()
        {
            return GetDataList<T>(SqlTableEntry, selectFields, new string[0], SharedOpenConnection, new SqlOrderBy[0], null, false);
        }

        /// <summary>
        /// 依据表或联接关系获取同类型的数据列表
        /// </summary>
        /// <param name="SqlTableEntry">表映射实例</param>
        /// <returns>同类型的数据列表</returns>
        public static T[] GetDataList<T>(this TableEntry SqlTableEntry)
            where T : TableEntry, new()
        {
            return GetDataList<T>(SqlTableEntry, new string[0], new string[0], null, new SqlOrderBy[0], null, false);
        }

        /// <summary>
        /// 依据表或联接关系获取同类型的数据列表
        /// </summary>
        /// <param name="SqlTableEntry">表映射实例</param>
        /// <param name="selectFields">列表获取的实体属性名集合，忽略属性将保持其默认值。</param>
        /// <returns>同类型的数据列表</returns>
        public static T[] GetDataList<T>(this TableEntry SqlTableEntry, string[] selectFields)
            where T : TableEntry, new()
        {
            return GetDataList<T>(SqlTableEntry, selectFields, new string[0], null, new SqlOrderBy[0], null, false);
        }

        /// <summary>
        /// 依据表或联接关系获取同类型的分页数据列表
        /// </summary>
        /// <typeparam name="T">基于TableEntry的实体类型</typeparam>
        /// <param name="SqlTableEntry">表映射实例</param>
        /// <param name="currentPage">当前页</param>
        /// <param name="PageSize">每页显示条数</param>
        /// <param name="totalRecord">返回当前实例类型的总记录条数</param>
        /// <returns>同类型的分页数据列表</returns>
        public static T[] GetDataList<T>(this TableEntry SqlTableEntry,
            int currentPage, int PageSize, ref long totalRecord)
            where T : TableEntry, new()
        {
            return GetDataList<T>(SqlTableEntry, null, currentPage, PageSize, null, null, ref totalRecord);
        }

        /// <summary>
        /// 依据表或联接关系获取同类型的分页数据列表
        /// </summary>
        /// <typeparam name="T">基于TableEntry的实体类型</typeparam>
        /// <param name="SqlTableEntry">表映射实例</param>
        /// <param name="currentPage">当前页</param>
        /// <param name="PageSize">每页显示条数</param>
        /// <param name="OrderSettings">针对提取数据的排序设置</param>
        /// <param name="totalRecord">返回当前实例类型的总记录条数</param>
        /// <returns>同类型的分页数据列表</returns>
        public static T[] GetDataList<T>(this TableEntry SqlTableEntry,
            int currentPage, int PageSize, SqlOrderBy[] OrderSettings, ref long totalRecord)
            where T : TableEntry, new()
        {
            return GetDataList<T>(SqlTableEntry, null, currentPage, PageSize, null, OrderSettings, ref totalRecord);
        }

        /// <summary>
        /// 依据表或联接关系获取同类型的分页数据列表
        /// </summary>
        /// <typeparam name="T">基于TableEntry的实体类型</typeparam>
        /// <param name="SqlTableEntry">表映射实例</param>
        /// <param name="selectFields">列表获取的实体属性名集合，忽略属性将保持其默认值。</param>
        /// <param name="currentPage">当前页</param>
        /// <param name="PageSize">每页显示条数</param>
        /// <param name="OrderSettings">针对提取数据的排序设置</param>
        /// <param name="totalRecord">返回当前实例类型的总记录条数</param>
        /// <returns>同类型的分页数据列表</returns>
        public static T[] GetDataList<T>(this TableEntry SqlTableEntry, string[] selectFields,
            int currentPage, int PageSize, SqlOrderBy[] OrderSettings, ref long totalRecord)
            where T : TableEntry, new()
        {
            return GetDataList<T>(SqlTableEntry, selectFields, currentPage, PageSize, null, OrderSettings, ref totalRecord);
        }

        /// <summary>
        /// 使用已打开的共享连接，依据表或联接关系获取同类型的分页数据列表
        /// </summary>
        /// <typeparam name="T">基于TableEntry的实体类型</typeparam>
        /// <param name="SqlTableEntry">表映射实例</param>
        /// <param name="selectFields">列表获取的实体属性名集合，忽略属性将保持其默认值。</param>
        /// <param name="currentPage">当前页</param>
        /// <param name="PageSize">每页显示条数</param>
        /// <param name="SharedOpenConnection">已打开的共享数据库连接，只使用不关闭及销毁。</param>
        /// <param name="OrderSettings">针对提取数据的排序设置</param>
        /// <returns>同类型的分页数据列表</returns>
        public static T[] GetDataList<T>(this TableEntry SqlTableEntry, string[] selectFields,
            int currentPage, int PageSize, DbConnection SharedOpenConnection, SqlOrderBy[] OrderSettings)
            where T : TableEntry, new()
        {
            long total = long.MaxValue;
            return GetDataList<T>(SqlTableEntry, selectFields, currentPage, PageSize, SharedOpenConnection, OrderSettings,
                ref total);
        }

        /// <summary>
        /// 使用已打开的共享连接，依据表或联接关系获取同类型的分页数据表格
        /// </summary>
        /// <param name="SqlTableEntry">表映射实例</param>
        /// <param name="selectFields">列表获取的实体属性名集合，忽略属性将保持其默认值。</param>
        /// <param name="currentPage">当前页</param>
        /// <param name="PageSize">每页显示条数</param>
        /// <param name="SharedOpenConnection">已打开的共享数据库连接，只使用不关闭及销毁。</param>
        /// <param name="OrderSettings">针对提取数据的排序设置</param>
        /// <param name="totalRecord">返回当前实例类型的总记录条数</param>
        /// <returns>满足条件的内容数据表</returns>
        public static DataTable GetDataTable(this TableEntry SqlTableEntry, string[] selectFields,
            int currentPage, int PageSize, DbConnection SharedOpenConnection, SqlOrderBy[] OrderSettings,
            ref long totalRecord)
        {
            AdoNetAdapter ormAdp = new AdoNetAdapter(SqlTableEntry);
            ISqlBuilderAdapter Builder = null;
            DbCommand cmd = GeneratorDbCommandWithBuilder(SqlTableEntry, SQLRowCommandType.Select, null, selectFields, OrderSettings, out Builder);
            ISqlDbAdapter dbAdp = ormAdp.GetSqlDbAdapter();
            DataTable dTab = new DataTable("Default");

            if (SharedOpenConnection != null)
            {
                cmd.Connection = SharedOpenConnection;
            }
            else
            {
                cmd.Connection = ormAdp.GetDbConnection(); ;
                cmd.Connection.Open();
            }

            if (dbAdp.HasImplementCustomPage())
            {
                ISqlDb sqlDb = ormAdp.GetSqlDbHelper();
                if (SharedOpenConnection != null)  sqlDb.CurrentConnection = SharedOpenConnection;
                dTab = dbAdp.GetPagedDataTable(sqlDb, Builder, EntryTableCache.GetTableCache(SqlTableEntry, Builder),
                    currentPage, PageSize, SharedOpenConnection, cmd);
                sqlDb.Dispose();
            }
            else
            {
                #region 使用ADO.NET适配器自带的分页实现
                using (DbDataAdapter adp = ormAdp.GetDbDataAdapter())
                {
                    adp.SelectCommand = cmd;
                    int startIndex = (currentPage <= 1) ? 0 : (currentPage - 1) * PageSize;
                    adp.Fill(startIndex, PageSize, dTab);
                }
                #endregion
            }

            if (totalRecord != long.MaxValue)
            {
                totalRecord = GetTotalRecord(SqlTableEntry, cmd.Connection, cmd);
            }

            if (SharedOpenConnection != null)
            {
                cmd.Connection.Close();
                cmd.Connection.Dispose();
            }
            cmd.Dispose();
            ormAdp.Dispose();

            return dTab;
        }

        /// <summary>
        /// 使用已打开的共享连接，依据表或联接关系获取同类型的数据表格
        /// </summary>
        /// <param name="SqlTableEntry">表映射实例</param>
        /// <param name="selectFields">列表获取的实体属性名集合，忽略属性将保持其默认值。</param>
        /// <param name="SharedOpenConnection">已打开的共享数据库连接，只使用不关闭及销毁。</param>
        /// <param name="OrderSettings">针对提取数据的排序设置</param>
        /// <returns></returns>
        public static DataTable GetDataTable(this TableEntry SqlTableEntry,
            string[] selectFields, DbConnection SharedOpenConnection, SqlOrderBy[] OrderSettings)
        {
            AdoNetAdapter ormAdp = new AdoNetAdapter(SqlTableEntry);
            DbCommand cmd = GeneratorDbCommand(SqlTableEntry, SQLRowCommandType.Select, null, selectFields, OrderSettings);
            if (SharedOpenConnection != null)
            {
                cmd.Connection = SharedOpenConnection;
            }
            else
            {
                cmd.Connection = ormAdp.GetDbConnection();
                cmd.Connection.Open();
            }
            DataTable dTab = new DataTable("Default");
            using (DbDataAdapter adp = ormAdp.GetDbDataAdapter())
            {
                adp.SelectCommand = cmd;
                adp.Fill(dTab);
            }
            if (SharedOpenConnection != null)
            {
                cmd.Connection.Close();
                cmd.Connection.Dispose();
            }
            cmd.Dispose();
            return dTab;
        }

        /// <summary>
        /// 使用已打开的共享连接，依据表或联接关系获取同类型的分页数据列表
        /// </summary>
        /// <typeparam name="T">基于TableEntry的实体类型</typeparam>
        /// <param name="SqlTableEntry">表映射实例</param>
        /// <param name="selectFields">列表获取的实体属性名集合，忽略属性将保持其默认值。</param>
        /// <param name="currentPage">当前页</param>
        /// <param name="PageSize">每页显示条数</param>
        /// <param name="SharedOpenConnection">已打开的共享数据库连接，只使用不关闭及销毁。</param>
        /// <param name="OrderSettings">针对提取数据的排序设置</param>
        /// <param name="totalRecord">返回当前实例类型的总记录条数</param>
        /// <returns></returns>
        public static T[] GetDataList<T>(this TableEntry SqlTableEntry, string[] selectFields,
            int currentPage, int PageSize, DbConnection SharedOpenConnection, SqlOrderBy[] OrderSettings,
            ref long totalRecord)
            where T : TableEntry, new()
        {
            List<T> resultList = new List<T>();
            DataTable dTab = GetDataTable(SqlTableEntry, selectFields, currentPage, PageSize, SharedOpenConnection,
                OrderSettings, ref totalRecord);

            if (dTab != null && dTab.Rows.Count > 0)
            {
                Type t = SqlTableEntry.GetType();
                string propName = "";

                DataRow dRow = null;
                PropertyInfo[] Properties = GenericCache<PropertyInfo[]>.Get(t.FullName, () => t.GetProperties());
                for (int i = 0; i < dTab.Rows.Count; i++)
                {
                    dRow = dTab.Rows[i];

                    #region 新建实例，并绑定相关数据。
                    T entry = new T();
                    entry.SerializeOnlyList = selectFields;
                    entry.IsInCollection = true;

                    foreach (PropertyInfo pi in Properties)
                    {
                        propName = pi.Name;
                        //所有命令操作忽略
                        if (HasAttribute(pi, typeof(CommandIgnoreAttribute), true))
                        {
                            continue;
                        }

                        //标记有映射转换
                        TableItemAttribute[] itemAttr = GetMemberInfoCustomAttributes<TableItemAttribute>(pi, true);
                        if (itemAttr.Length > 0)
                        {
                            propName = itemAttr[0].BindField;
                        }
                        try
                        {
                            //有可能没有对应字段名称
                            if (dTab.Columns.Contains(propName))
                            {
                                pi.SetValue(entry, dRow[propName], null);
                            }
                        }
                        catch (Exception) { }
                    }
                    #endregion

                    resultList.Add(entry);
                }
            }
            dTab.Dispose();

            return resultList.ToArray();
        }

        /// <summary>
        /// 使用已打开的共享连接，依据表或联接关系获取同类型的分页数据列表
        /// </summary>
        /// <typeparam name="T">基于TableEntry的实体类型</typeparam>
        /// <param name="SqlTableEntry">表映射实例</param>
        /// <param name="selectFields">列表获取的实体属性名集合，忽略属性将保持其默认值。</param>
        /// <param name="currentPage">当前页</param>
        /// <param name="PageSize">每页显示条数</param>
        /// <param name="SharedOpenConnection">已打开的共享数据库连接，只使用不关闭及销毁。</param>
        /// <returns></returns>
        public static T[] GetDataList<T>(this T SqlTableEntry, string[] selectFields,
            int currentPage, int PageSize, DbConnection SharedOpenConnection)
            where T : TableEntry, new()
        {
            return GetDataList<T>(SqlTableEntry, selectFields, currentPage, PageSize, SharedOpenConnection, null);
        }

        /// <summary>
        /// 依据表或联接关系获取同类型的分页数据列表
        /// </summary>
        /// <param name="SqlTableEntry">表映射实例</param>
        /// <param name="selectFields">列表获取的实体属性名集合，忽略属性将保持其默认值。</param>
        /// <param name="currentPage">当前页</param>
        /// <param name="PageSize">每页显示条数</param>
        /// <returns></returns>
        public static T[] GetDataList<T>(this T SqlTableEntry, string[] selectFields,
            int currentPage, int PageSize)
            where T : TableEntry, new()
        {
            return GetDataList<T>(SqlTableEntry, selectFields, currentPage, PageSize, null);
        }

        #endregion
	}
}
